import { Op } from 'sequelize';
import Tag from '../models/Tag';
import Article from '../models/Article';
import { redisClient } from '../database';

// 标签查询参数接口
interface TagQueryParams {
  keyword?: string;
  sort_by?: string;
  sort_order?: 'ASC' | 'DESC';
  page?: number;
  limit?: number;
  status?: 'active' | 'inactive';
}

// 标签创建接口
interface TagCreateParams {
  name: string;
  slug: string;
  description?: string;
  status?: 'active' | 'inactive';
}

// 标签更新接口
interface TagUpdateParams extends Partial<TagCreateParams> {}

class TagService {
  /**
   * 获取标签列表
   * @param params 查询参数
   * @returns 标签列表和总数
   */
  async getTags(params: TagQueryParams = {}) {
    const {
      keyword,
      sort_by = 'article_count',
      sort_order = 'DESC',
      page = 1,
      limit = 50,
      status,
    } = params;
    
    const offset = (page - 1) * limit;
    
    // 构建查询条件
    const where: any = {};
    
    if (keyword) {
      where[Op.or] = [
        { name: { [Op.like]: `%${keyword}%` } },
        { slug: { [Op.like]: `%${keyword}%` } },
        { description: { [Op.like]: `%${keyword}%` } },
      ];
    }
    
    if (status) {
      where.status = status;
    }
    
    // 执行查询
    const { rows, count } = await Tag.findAndCountAll({
      where,
      order: [[sort_by, sort_order]],
      limit,
      offset,
    });
    
    return {
      tags: rows,
      total: count,
      page,
      limit,
      total_pages: Math.ceil(count / limit),
    };
  }
  
  /**
   * 获取热门标签
   * @param limit 返回数量
   * @returns 热门标签列表
   */
  async getHotTags(limit = 20) {
    // 先尝试从缓存获取
    const cacheKey = `hot_tags:${limit}`;
    const cachedTags = await redisClient.get(cacheKey);
    
    if (cachedTags) {
      return JSON.parse(cachedTags);
    }
    
    // 缓存未命中，从数据库获取
    const tags = await Tag.findAll({
      where: {
        status: 'active',
        article_count: { [Op.gt]: 0 },
      },
      order: [['article_count', 'DESC']],
      limit,
    });
    
    // 缓存标签
    await redisClient.set(cacheKey, JSON.stringify(tags), {
      EX: 3600, // 缓存1小时
    });
    
    return tags;
  }
  
  /**
   * 根据ID获取标签详情
   * @param id 标签ID
   * @returns 标签详情
   */
  async getTagById(id: string) {
    // 先尝试从缓存获取
    const cacheKey = `tag:${id}`;
    const cachedTag = await redisClient.get(cacheKey);
    
    if (cachedTag) {
      return JSON.parse(cachedTag);
    }
    
    // 缓存未命中，从数据库获取
    const tag = await Tag.findByPk(id);
    
    if (!tag) {
      throw new Error('标签不存在');
    }
    
    // 缓存标签
    await redisClient.set(cacheKey, JSON.stringify(tag), {
      EX: 3600, // 缓存1小时
    });
    
    return tag;
  }
  
  /**
   * 根据Slug获取标签详情
   * @param slug 标签Slug
   * @returns 标签详情
   */
  async getTagBySlug(slug: string) {
    // 先尝试从缓存获取
    const cacheKey = `tag:slug:${slug}`;
    const cachedTag = await redisClient.get(cacheKey);
    
    if (cachedTag) {
      return JSON.parse(cachedTag);
    }
    
    // 缓存未命中，从数据库获取
    const tag = await Tag.findOne({
      where: { slug },
    });
    
    if (!tag) {
      throw new Error('标签不存在');
    }
    
    // 缓存标签
    await redisClient.set(cacheKey, JSON.stringify(tag), {
      EX: 3600, // 缓存1小时
    });
    
    // 同时更新ID缓存
    await redisClient.set(`tag:${tag.id}`, JSON.stringify(tag), {
      EX: 3600, // 缓存1小时
    });
    
    return tag;
  }
  
  /**
   * 创建标签
   * @param params 标签参数
   * @returns 创建的标签
   */
  async createTag(params: TagCreateParams) {
    const {
      name,
      slug,
      description,
      status = 'active',
    } = params;
    
    // 检查slug是否已存在
    const existingTag = await Tag.findOne({
      where: { slug },
    });
    
    if (existingTag) {
      throw new Error('标签slug已存在，请修改后重试');
    }
    
    // 创建标签
    const tag = await Tag.create({
      name,
      slug,
      description,
      status,
      article_count: 0,
    });
    
    return tag;
  }
  
  /**
   * 更新标签
   * @param id 标签ID
   * @param params 更新参数
   * @returns 更新后的标签
   */
  async updateTag(id: string, params: TagUpdateParams) {
    const tag = await Tag.findByPk(id);
    
    if (!tag) {
      throw new Error('标签不存在');
    }
    
    const {
      name,
      slug,
      description,
      status,
    } = params;
    
    // 检查slug是否已被其他标签使用
    if (slug && slug !== tag.slug) {
      const existingTag = await Tag.findOne({
        where: {
          slug,
          id: { [Op.ne]: id },
        },
      });
      
      if (existingTag) {
        throw new Error('标签slug已存在，请修改后重试');
      }
    }
    
    // 准备更新数据
    const updateData: any = {};
    
    if (name !== undefined) updateData.name = name;
    if (slug !== undefined) updateData.slug = slug;
    if (description !== undefined) updateData.description = description;
    if (status !== undefined) updateData.status = status;
    
    // 更新标签
    await tag.update(updateData);
    
    // 清除缓存
    await redisClient.del(`tag:${id}`);
    if (tag.slug) await redisClient.del(`tag:slug:${tag.slug}`);
    if (slug && slug !== tag.slug) await redisClient.del(`tag:slug:${slug}`);
    await redisClient.del('hot_tags:*'); // 清除所有热门标签缓存
    
    // 重新加载标签
    const updatedTag = await Tag.findByPk(id);
    
    return updatedTag;
  }
  
  /**
   * 删除标签
   * @param id 标签ID
   * @returns 删除结果
   */
  async deleteTag(id: string) {
    const tag = await Tag.findByPk(id);
    
    if (!tag) {
      throw new Error('标签不存在');
    }
    
    // 获取标签下的文章数量
    const articleCount = await this.getTagArticleCount(id);
    
    // 如果有文章使用该标签，则拒绝删除
    if (articleCount > 0) {
      throw new Error(`该标签下有${articleCount}篇文章，无法删除`);
    }
    
    // 删除标签
    await tag.destroy();
    
    // 清除缓存
    await redisClient.del(`tag:${id}`);
    if (tag.slug) await redisClient.del(`tag:slug:${tag.slug}`);
    await redisClient.del('hot_tags:*'); // 清除所有热门标签缓存
    
    return { success: true, message: '删除标签成功' };
  }
  
  /**
   * 批量删除标签
   * @param ids 标签ID数组
   * @param force 是否强制删除（忽略关联文章）
   * @returns 删除结果
   */
  async deleteTagsBatch(ids: string[], force = false) {
    if (!ids.length) {
      throw new Error('请选择要删除的标签');
    }
    
    // 如果不是强制删除，检查是否有文章使用这些标签
    if (!force) {
      for (const id of ids) {
        const articleCount = await this.getTagArticleCount(id);
        if (articleCount > 0) {
          const tag = await Tag.findByPk(id);
          throw new Error(`标签"${tag?.name}"下有${articleCount}篇文章，无法删除`);
        }
      }
    }
    
    // 批量删除标签前，获取所有标签信息用于清除缓存
    const tags = await Tag.findAll({
      where: { id: { [Op.in]: ids } },
      attributes: ['id', 'slug'],
    });
    
    // 批量删除标签
    await Tag.destroy({
      where: { id: { [Op.in]: ids } },
    });
    
    // 清除缓存
    for (const tag of tags) {
      await redisClient.del(`tag:${tag.id}`);
      if (tag.slug) await redisClient.del(`tag:slug:${tag.slug}`);
    }
    await redisClient.del('hot_tags:*'); // 清除所有热门标签缓存
    
    return { success: true, message: `成功删除${ids.length}个标签` };
  }
  
  /**
   * 合并标签
   * @param sourceIds 源标签ID数组
   * @param targetId 目标标签ID
   * @returns 合并结果
   */
  async mergeTags(sourceIds: string[], targetId: string) {
    if (!sourceIds.length) {
      throw new Error('请选择要合并的源标签');
    }
    
    if (sourceIds.includes(targetId)) {
      throw new Error('源标签不能包含目标标签');
    }
    
    // 检查目标标签是否存在
    const targetTag = await Tag.findByPk(targetId);
    if (!targetTag) {
      throw new Error('目标标签不存在');
    }
    
    // 获取所有源标签
    const sourceTags = await Tag.findAll({
      where: { id: { [Op.in]: sourceIds } },
    });
    
    if (sourceTags.length !== sourceIds.length) {
      throw new Error('部分源标签不存在');
    }
    
    // 获取使用源标签的文章
    const transaction = await Tag.sequelize!.transaction();
    
    try {
      // 获取源标签的文章总数
      let totalArticles = 0;
      
      // 为每个源标签处理关联文章
      for (const sourceId of sourceIds) {
        // 获取使用该源标签的所有文章ID
        const articleTags = await Article.sequelize!.models.ArticleTag.findAll({
          where: { tag_id: sourceId },
          transaction,
        });
        
        totalArticles += articleTags.length;
        
        // 为这些文章关联目标标签（如果尚未关联）
        for (const articleTag of articleTags) {
          const exists = await Article.sequelize!.models.ArticleTag.findOne({
            where: {
              article_id: articleTag.article_id,
              tag_id: targetId,
            },
            transaction,
          });
          
          if (!exists) {
            await Article.sequelize!.models.ArticleTag.create({
              article_id: articleTag.article_id,
              tag_id: targetId,
            }, { transaction });
          }
          
          // 删除源标签关联
          await articleTag.destroy({ transaction });
        }
      }
      
      // 更新目标标签的文章数量
      await targetTag.update({
        article_count: await this.getTagArticleCount(targetId),
      }, { transaction });
      
      // 删除源标签
      await Tag.destroy({
        where: { id: { [Op.in]: sourceIds } },
        transaction,
      });
      
      await transaction.commit();
      
      // 清除缓存
      await redisClient.del(`tag:${targetId}`);
      if (targetTag.slug) await redisClient.del(`tag:slug:${targetTag.slug}`);
      for (const tag of sourceTags) {
        await redisClient.del(`tag:${tag.id}`);
        if (tag.slug) await redisClient.del(`tag:slug:${tag.slug}`);
      }
      await redisClient.del('hot_tags:*'); // 清除所有热门标签缓存
      
      return {
        success: true,
        message: `成功将${sourceTags.length}个标签合并到"${targetTag.name}"，共涉及${totalArticles}篇文章`,
      };
    } catch (error) {
      await transaction.rollback();
      throw error;
    }
  }
  
  /**
   * 获取标签关联的文章数量
   * @param tagId 标签ID
   * @returns 文章数量
   */
  private async getTagArticleCount(tagId: string) {
    return await Article.sequelize!.models.ArticleTag.count({
      where: { tag_id: tagId },
    });
  }
  
  /**
   * 同步标签的文章数量
   * @param tagId 标签ID，如果未提供则同步所有标签
   * @returns 同步结果
   */
  async syncTagArticleCount(tagId?: string) {
    const transaction = await Tag.sequelize!.transaction();
    
    try {
      const tags = tagId
        ? [await Tag.findByPk(tagId, { transaction })]
        : await Tag.findAll({ transaction });
      
      if (tagId && !tags[0]) {
        throw new Error('标签不存在');
      }
      
      let updatedCount = 0;
      
      for (const tag of tags) {
        if (!tag) continue;
        
        const articleCount = await this.getTagArticleCount(tag.id);
        
        if (tag.article_count !== articleCount) {
          await tag.update({ article_count: articleCount }, { transaction });
          updatedCount++;
          
          // 清除缓存
          await redisClient.del(`tag:${tag.id}`);
          if (tag.slug) await redisClient.del(`tag:slug:${tag.slug}`);
        }
      }
      
      await transaction.commit();
      
      // 清除热门标签缓存
      await redisClient.del('hot_tags:*');
      
      return {
        success: true,
        message: tagId
          ? `标签文章数量同步成功`
          : `成功同步${updatedCount}个标签的文章数量`,
      };
    } catch (error) {
      await transaction.rollback();
      throw error;
    }
  }
}

export default new TagService(); 